package com.example.g7640.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.g7640.builder.ResultBuilder;
import com.example.g7640.convert.OrderConvertor;
import com.example.g7640.dto.OrderDetailDto;
import com.example.g7640.dto.OrderDto;
import com.example.g7640.dto.OrderQuery;
import com.example.g7640.dto.ProductDto;
import com.example.g7640.dto.common.PageResult;
import com.example.g7640.entity.Order;
import com.example.g7640.entity.OrderDetail;
import com.example.g7640.entity.Product;
import com.example.g7640.enums.OrderStatusEnum;
import com.example.g7640.exception.BizException;
import com.example.g7640.mapper.OrderDetailMapper;
import com.example.g7640.mapper.OrderMapper;
import com.example.g7640.mapper.ProductMapper;
import com.example.g7640.service.OrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.g7640.util.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author  
 * @since 2024-03-18
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Resource
    OrderMapper orderMapper;

    @Resource
    OrderDetailMapper orderDetailMapper;

    @Resource
    ProductMapper productMapper;

    @Resource
    OrderConvertor orderConvertor;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createOrder(OrderDto orderDto) {
        Order order = BaseConvertor.convert(orderDto,Order.class);
        List<OrderDetail> detailList = BaseConvertor.convertList(orderDto.getDetailList(),OrderDetail.class);
        RoleCheckUtil.checkCustomer();
        order.setCid(SessionLocal.getLoginId());
        LocalDateTime now = LocalDateTime.now();
        order.setCreatedAt(now);
        order.setUpdatedAt(now);
        order.setStatus(OrderStatusEnum.created.getIndex());
        orderMapper.insert(order);

        //处理orderDetail
        List<Integer> pidList = StreamUtil.convertList(detailList, e -> e.getPid());
        List<Product> products = productMapper.selectBatchIds(pidList);
        Map<Integer, Product> productMap = StreamUtil.listToMap(products, e -> e.getId());
        for (OrderDetail orderDetail : detailList) {
            orderDetail.setCreatedAt(now);
            orderDetail.setUpdatedAt(now);
            orderDetail.setStatus(OrderStatusEnum.created.getIndex());
            orderDetail.setOid(order.getId());
            Product product = productMap.get(orderDetail.getPid());
            if(product == null){
                throw new BizException("product is not exist pid=" + orderDetail.getPid());
            }
            if(product.getInventory() < orderDetail.getQuantity()){
                throw new BizException("Insufficient inventory, pid=" + orderDetail.getQuantity());
            }
            int tmp = productMapper.reduceInventory(product.getId(), orderDetail.getQuantity());
            if(tmp == 0){
                throw new BizException("reduce inventory failed, prductId=" + product.getId());
            }
            orderDetailMapper.insert(orderDetail);
        }
    }

    @Override
    public OrderDto queryOrderById(Integer id) {
        Order order = orderMapper.selectById(id);
        List<OrderDetail> detailList = queryOrderDetailByOrderId(id);
        return orderConvertor.doToDto(order,detailList);
    }

    @Override
    public void deleteOrder(Integer id) {
        Order order = orderMapper.selectById(id);
        if(order == null){
            throw new BizException("order not exist");
        }
        order.setStatus(OrderStatusEnum.deleted.getIndex());
        order.setUpdatedAt(LocalDateTime.now());
        orderMapper.updateById(order);
    }

    @Override
    public PageResult<List<OrderDto>> queryOrder(OrderQuery query) {
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        if(query.getCid() != null){
            wrapper.eq(Order::getCid,query.getCid());
        }
        if(query.getCreateTimeStart() != null && query.getCreateTimeEnd() != null){
            wrapper.between(Order::getCreatedAt,query.getCreateTimeStart(),query.getCreateTimeEnd());
        }
        if(query.getStatus() != null){
            wrapper.eq(Order::getStatus,query.getStatus());
        }
        if(query.getVid() != null){
            List<Integer> orderIds = this.queryOrderIdByVid(query.getVid());
            if(CollectionUtils.isEmpty(orderIds)){
                return ResultBuilder.buildPageSuccess(0,query.getPageNo(),query.getPageSize(), Collections.emptyList());
            }
            wrapper.in(Order::getId,orderIds);
        }
        IPage iPage = orderMapper.selectPage(PageUtil.pageToMybatisPage(query), wrapper);
        return PageUtil.mybatisPageToPage(iPage,OrderDto.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void cancelOrder(Integer id) {
        Order order = orderMapper.selectById(id);
        if(order == null){
            throw new BizException("order not exist");
        }
        order.setStatus(OrderStatusEnum.canceled.getIndex());
        orderMapper.updateById(order);

        List<OrderDetail> orderDetails = queryOrderDetailByOrderId(id);
        for (OrderDetail orderDetail : orderDetails) {
            productMapper.addInventory(orderDetail.getPid(),orderDetail.getQuantity());
        }

    }

    @Override
    public void updateOrderStatus(OrderDto orderDto) {
        Order order = orderMapper.selectById(orderDto.getId());
        if(order == null){
            throw new BizException("order not exist");
        }
        order.setStatus(orderDto.getStatus());
        order.setUpdatedAt(LocalDateTime.now());
        orderMapper.updateById(order);
    }

    @Override
    public void updateOrderDetail(OrderDetailDto orderDetailDto) {
        OrderDetail orderDetail = orderDetailMapper.selectById(orderDetailDto.getId());
        orderDetail.setStatus(orderDetailDto.getStatus());
        orderDetail.setDrid(orderDetailDto.getDrid());
        orderDetailMapper.updateById(orderDetail);
    }


    private List<OrderDetail> queryOrderDetailByOrderId(Integer orderId){
        LambdaQueryWrapper<OrderDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderDetail::getOid,orderId);
        return orderDetailMapper.selectList(wrapper);
    }


    private List<Integer> queryOrderIdByVid(Integer vid){
        LambdaQueryWrapper<OrderDetail> wrap = new LambdaQueryWrapper<>();
        wrap.eq(OrderDetail::getVid,vid);
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(wrap);
        List<Integer> orderIds = orderDetails.stream().map(e -> e.getOid()).distinct().collect(Collectors.toList());
        return orderIds;
    }
}
